package utils

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"time"
)

// 任务状态
const (
	Success int8 = 0
	Waiting int8 = 10
	Running int8 = 20
	Skipped int8 = 30
	Warming int8 = 40
	Timeout int8 = 50
	Cancel  int8 = 60
	Failed  int8 = 70
	Crash   int8 = 80
)

var StatusValue = []int8{Success, Waiting, Running, Skipped, Warming, Timeout, Cancel, Failed, Crash}

// LocalRunCommand 在本地执行命令，并根据指定的超时时间返回执行结果。
// 该函数接收一个命令字符串和一个超时时间（秒为单位），返回命令执行状态码、输出和可能的错误。
// 参数:
//
//	command - 待执行的命令字符串，例如 "ls -l"。
//	timeout - 命令执行的最大允许时间，单位为秒。如果为0，则不设置超时。
//
// 返回值:
//
//	int8 - 命令执行状态码，表示命令执行成功或失败。
//	string - 命令的合并输出（包括标准输出和标准错误）。
//	error - 如果命令执行失败或出现错误，返回错误信息。
func LocalRunCommand(command string, timeout uint32) (int8, string, error) {
	parts := strings.SplitN(command, " ", 2)
	if len(parts) < 1 {
		return Failed, "", fmt.Errorf("无效的命令: %s", command)
	}
	cmdName := parts[0]
	args := []string{}
	if len(parts) > 1 {
		args = append(args, parts[1])
	}

	var ctx context.Context
	if timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()
	} else {
		ctx = context.Background()
	}

	cmd := exec.CommandContext(ctx, cmdName, args...)
	output, excErr := cmd.CombinedOutput()
	if excErr != nil {
		outputStr := string(output)
		if ctx.Err() == context.DeadlineExceeded {
			return Timeout, outputStr, fmt.Errorf("命令执行超时: %s, 超时时间: %d秒", command, timeout)
		}
		return Failed, outputStr, fmt.Errorf("命令执行失败: %s, 错误详情: %v", command, excErr)
	}
	return Success, string(output), nil
}

// StopServerByPID 尝试停止指定PID的服务器进程。
// 该函数首先尝试通过发送SIGTERM信号优雅地停止进程。
// 如果进程在指定的超时时间内未退出，则通过发送SIGKILL信号强制终止。
// 参数:
//
//	pid - 要停止的进程的PID。
//	timeout - 等待进程响应信号并退出的最大时间（秒）。
//
// 返回值:
//
//	error - 如果停止进程时遇到任何错误，返回相应的错误。
func StopServerByPID(pid uint16, timeout uint8) error {
	process, findProErr := os.FindProcess(int(pid))
	if findProErr != nil {
		return fmt.Errorf("无法找到进程 %d: %v", pid, findProErr)
	}
	if singErr := process.Signal(syscall.SIGTERM); singErr != nil {
		return fmt.Errorf("无法发送 SIGTERM 信号到进程 %d: %v", pid, singErr)
	}
	done := make(chan struct{})
	go func() {
		_, waitErr := process.Wait()
		if waitErr != nil {
			fmt.Printf("等待进程 %d 退出时出错: %v\n", pid, waitErr)
		}
		close(done)
	}()
	select {
	case <-done:
		return nil
	case <-time.After(time.Duration(timeout) * time.Second):
		if killChildErr := killChildren(pid); killChildErr != nil {
			return fmt.Errorf("无法杀死子进程: %v", killChildErr)
		}
		if err := process.Signal(syscall.SIGKILL); err != nil {
			return fmt.Errorf("无法发送 SIGKILL 信号到进程 %d: %v", pid, err)
		}
		select {
		case <-done:
			return nil
		case <-time.After(time.Duration(timeout) * time.Second):
			return fmt.Errorf("进程 %d 强杀失败", pid)
		}
	}
}

// killChildren 根据父进程ID终止其所有子进程。
// 参数 parentPid 是需要终止子进程的父进程ID。
// 返回错误如果执行过程中遇到任何问题，否则返回nil。
func killChildren(parentPid uint16) error {
	cmd := exec.Command("ps", "-o", "pid=", "--ppid", fmt.Sprintf("%d", parentPid))
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("无法获取子进程列表: %v", err)
	}
	pids := strings.Split(strings.TrimSpace(string(output)), "\n")
	for _, pidStr := range pids {
		if pidStr == "" {
			continue
		}
		childPid, err := strconv.Atoi(pidStr)
		if err != nil {
			return fmt.Errorf("无法解析子进程 PID: %v", err)
		}
		childProcess, err := os.FindProcess(childPid)
		if err != nil {
			return fmt.Errorf("无法找到子进程 %d: %v", childPid, err)
		}
		if err := childProcess.Signal(syscall.SIGKILL); err != nil {
			return fmt.Errorf("无法发送 SIGKILL 信号到子进程 %d: %v", childPid, err)
		}
	}
	return nil
}
